home *** CD-ROM | disk | FTP | other *** search
/ MacTech 1 to 12 / MacTech-vol-1-12.toast / Source / MacTech® Magazine / Volume 10 - 1994 / 10.12 Dec 94 / BetterWindow / BetterWindow.cp next >
Encoding:
Text File  |  1994-07-24  |  29.0 KB  |  868 lines  |  [TEXT/MPS ]

  1. //----------------------------------------------------------------------------------------
  2. // Nothing.cp
  3. // Copyright © 1986-1992 by Apple Computer, Inc. All rights reserved.
  4. //
  5. // This is a very small sample application which can save files on disk and can print. The
  6. // application's windows each contain the word 'MacApp', in large type, and are framed by
  7. // a large gray border. Documents can be saved on disk and later reopened, but the only
  8. // document-specific information saved in the disk file is the Print information, which
  9. // means that if you set print specifications using the Page Setup dialog for a document,
  10. // and then save the document (using Save As or Save a Copy), those specifications are
  11. // saved on disk, and when you reopen the document you will find those same print
  12. // specifications holding for the document. All applications that create views
  13. // procedurally need to reimplement at least three methods:
  14. //
  15. //  TApplication.DoMakeDocument    --    Launches the appropriate type of Document object
  16. //  TDocument.DoMakeViews        --    Launches the appropriate type of View and window objects
  17. //  TView.Draw                    --    Draws the contents of a view
  18. //
  19. // This application, however, consists of the reimplementation of only one method -
  20. // TView.Draw. This is possible since the view is created from templates; MacApp supplies
  21. // the default 'view' resource. So in a sense this application is the smallest possible
  22. // MacApp application.
  23. //----------------------------------------------------------------------------------------
  24.  
  25. #ifndef __TYPES__
  26. #include <Types.h>
  27. #endif
  28.  
  29. #ifndef __FONTS__
  30. #include <Fonts.h>
  31. #endif
  32.  
  33. #ifndef __QUICKDRAW__
  34. #include <QuickDraw.h>
  35. #endif
  36.  
  37. #ifndef __MACAPPTYPES__
  38. #include <MacAppTypes.h>
  39. #endif
  40.  
  41. #ifndef __UMACAPPGLOBALS
  42. #include <UMacAppGlobals.h>
  43. #endif
  44.  
  45. #ifndef __UMACAPPUTILITIES__
  46. #include <UMacAppUtilities.h>
  47. #endif
  48.  
  49. #ifndef __UERRORMGR__
  50. #include <UErrorMgr.h>
  51. #endif
  52.  
  53. #ifndef __UAPPLICATION__
  54. #include <UApplication.h>
  55. #endif
  56.  
  57. #ifndef __UVIEW__
  58. #include <UView.h>
  59. #endif
  60.  
  61. #ifndef __UPRINTING__
  62. #include <UPrinting.h>
  63. #endif
  64.  
  65. //----------------------------------------------------------------------------------------
  66. // Constants
  67. //----------------------------------------------------------------------------------------
  68.  
  69. const OSType kSignature = 'SS01';                // Application signature
  70. const OSType kFileType = 'SF01';                // File-type code used for document files
  71.                                                 // created by this application
  72.  
  73.  
  74. //----------------------------------------------------------------------------------------
  75. // TNothingApplication:
  76. //----------------------------------------------------------------------------------------
  77.  
  78. class TNothingApplication : public TApplication
  79. {
  80. public:
  81.     virtual pascal void INothingApplication();
  82. };
  83.  
  84.  
  85. //----------------------------------------------------------------------------------------
  86. // TDefaultView:
  87. //----------------------------------------------------------------------------------------
  88.  
  89. VRect gBaseViewSize(0, 0, 489, 134);
  90. VRect gCurrentViewSize(0, 0, 489, 134);
  91.  
  92. class TDefaultView : public TView
  93. {
  94. public:
  95.     virtual pascal void Draw(const VRect& area);
  96.         // Draws the view seen in the window. Every nonblank view MUST override this
  97.         // method.
  98.     virtual pascal void CalcMinFrame(    VRect&        minFrame);
  99.     virtual pascal void DoKeyEvent(    TToolboxEvent*        event);
  100. };
  101.  
  102. //----------------------------------------------------------------------------------------
  103. // TBetterWindow:
  104. //----------------------------------------------------------------------------------------
  105.  
  106. class TBetterWindow : public TWindow {
  107.     private:
  108.         Boolean                                    fSavePosition;
  109.         Boolean                                    fInDefaultPosition;
  110.         
  111.     public:
  112.         // TObject overrides
  113.                                                 TBetterWindow();
  114.         virtual pascal void                        Initialize();
  115.         virtual pascal void                        IBetterWindow(    TDocument*    itsDocument,
  116.                                                             WindowPtr    itsWMgrWindow,
  117.                                                             Boolean        canResize,
  118.                                                             Boolean        canClose,
  119.                                                             Boolean        disposeOnFree);
  120.         virtual pascal void                        Free();
  121.         
  122.         // no TEventHandler overrides
  123.         
  124.         // no TCommandHandler overrides
  125.         
  126.         // TView overrides
  127.         virtual pascal void                        DoPostCreate(    TDocument*    itsDocument);
  128.     
  129.         // TWindow overrides
  130.         virtual pascal void                        SimpleStagger(    CPoint        delta,
  131.                                                                 short&        count);
  132.         
  133.         virtual pascal void                        GetStandardStateFrame(    const VRect&    boundingRect,
  134.                                                                         VRect&            stdFrame);
  135.         virtual pascal void                        Zoom(    short        partCode);
  136.         virtual pascal void                        Close();
  137.         
  138.         virtual pascal void                        MoveByUser(    const VPoint&        theMouse);
  139.         virtual pascal void                        ResizeByUser(    const VPoint&        theMouse);
  140.         virtual pascal void                        ZoomByUser(    const VPoint&        theMouse,
  141.                                                             short                partCode);
  142.         
  143.         // new methods
  144.         virtual pascal void                        MarkPositionAsDirty();
  145.         
  146.     private:
  147.         virtual pascal void                        GetMonitorInfo(    WindowPtr        frontWindow,
  148.                                                                 CRect&            monitorRect);
  149.         virtual pascal void                        MakePositionUnique(    CPoint&            newPos,
  150.                                                                     CPoint            ulhc,
  151.                                                                     CPoint            delta,
  152.                                                                     const CRect&    monitorRect);
  153.         
  154.         virtual pascal Boolean                    IsPositionUnique(    CPoint            pos);
  155.         
  156.         virtual pascal Boolean                    WillWindowFit(    CPoint            pos,
  157.                                                                 const CRect&    monitorRect);
  158.         virtual pascal Boolean                    PositionIsSaved();
  159.         virtual pascal Boolean                    RestorePosition();
  160.         virtual pascal void                        SavePosition();
  161. };
  162.  
  163.  
  164.  
  165. //========================================================================================
  166. // CLASS TNothingApplication
  167. //========================================================================================
  168.  
  169.  
  170. //----------------------------------------------------------------------------------------
  171. // TNothingApplication::INothingApplication: 
  172. //----------------------------------------------------------------------------------------
  173. pascal void TNothingApplication::INothingApplication()
  174. {
  175.     inherited::IApplication(kFileType, kSignature);
  176.     
  177.     // So my view will be substituted when MacApp® creates the "default view"
  178.     
  179.     RegisterStdType("TDefaultView", 'dflt');
  180.     RegisterStdType("TBetterWindow", kStdWindow);
  181.  
  182.     // So the linker doesn't dead strip class info 
  183.     
  184.     if (gDeadStripSuppression) {
  185.         macroDontDeadStrip(TDefaultView)
  186.         macroDontDeadStrip(TBetterWindow)
  187.     }
  188. } // TNothingApplication::INothingApplication 
  189.  
  190.  
  191.  
  192. //========================================================================================
  193. // CLASS TDefaultView
  194. //========================================================================================
  195.  
  196.  
  197. //----------------------------------------------------------------------------------------
  198. // TDefaultView::Draw: 
  199. //----------------------------------------------------------------------------------------
  200. pascal void TDefaultView::Draw(const VRect&)
  201. {
  202.     CStr255 title = "BetterWindow";
  203.     PicHandle        thePic;
  204.     CRect            qdRect;
  205.     short            textWidth;
  206.     CTemporaryRegion    saveClip;
  207.  
  208.     this->ViewToQDRect(gBaseViewSize, qdRect);
  209.     
  210.     // remove all clipping while recording the picture
  211.     GetClip(saveClip);
  212.     ClipRect(CRect(-32767, -32767, 32767, 32767));
  213.     
  214.     thePic = OpenPicture(qdRect);
  215.     
  216.     PenNormal();
  217.     PenSize(10, 10);
  218.     PenPat(&qd.dkGray);
  219.  
  220.     // Draw a dark gray frame
  221.     FrameRect(qdRect);
  222.  
  223.     // Set font and size for subsequent display in the window
  224.     
  225.     TextFont(times);    // a nice outline font
  226.     TextSize(72);
  227.  
  228.     textWidth = StringWidth(title);
  229.     
  230.     MoveTo(qdRect.left + (qdRect.GetLength(hSel) - textWidth) / 2, 90);
  231.     DrawString(title);
  232.     // Restore the pen state.
  233.     PenNormal();
  234.     
  235.     ClosePicture();
  236.     
  237.     SetClip(saveClip);
  238.     
  239.     this->GetQDExtent(qdRect);
  240.     DrawPicture(thePic, qdRect);
  241.     
  242.     KillPicture(thePic);
  243. } // TDefaultView::Draw 
  244.  
  245. //----------------------------------------------------------------------------------------
  246. // TDefaultView::CalcMinFrame: 
  247. //----------------------------------------------------------------------------------------
  248. pascal void TDefaultView::CalcMinFrame(VRect&        minFrame)
  249. {
  250.     minFrame = gCurrentViewSize;
  251. } // TDefaultView::CalcMinFrame 
  252.  
  253. //----------------------------------------------------------------------------------------
  254. // TDefaultView::DoKeyEvent: 
  255. //----------------------------------------------------------------------------------------
  256. pascal void TDefaultView::DoKeyEvent(TToolboxEvent*        event)
  257. {
  258.     switch (event->fCharacter) {
  259.         case 'u':
  260.         case 'U':
  261.             gCurrentViewSize.right += 50;
  262.             gCurrentViewSize.bottom += 25;
  263.             this->ForceRedraw();
  264.             this->AdjustFrame();
  265.             break;
  266.         
  267.         case 'd':
  268.         case 'D':
  269.             gCurrentViewSize.right -= 50;
  270.             gCurrentViewSize.bottom -= 25;
  271.             this->ForceRedraw();
  272.             this->AdjustFrame();
  273.             break;
  274.         
  275.         default:
  276.             inherited :: DoKeyEvent(event);
  277.     }
  278. } // TDefaultView::DoKeyEvent 
  279.  
  280.  
  281. //========================================================================================
  282. // CLASS TBetterWindow
  283. //========================================================================================
  284.  
  285. //----------------------------------------------------------------------------------------
  286. // INIT AND FREE METHODS
  287. //----------------------------------------------------------------------------------------
  288.  
  289. TBetterWindow :: TBetterWindow()
  290.     {
  291.         // empty constructor provided to satisfy compiler
  292.     }
  293.  
  294. pascal void TBetterWindow :: Initialize()
  295.     // Calls inherited.  No unique initialization.
  296.     {
  297.         inherited :: Initialize();
  298.         fSavePosition = false;
  299.         fInDefaultPosition = true;
  300.     }
  301.  
  302. pascal void TBetterWindow :: IBetterWindow(    TDocument*        itsDocument,
  303.                                     WindowPtr        itsWMgrWindow,
  304.                                     Boolean            canResize,
  305.                                     Boolean            canClose,
  306.                                     Boolean            disposeOnFree)
  307.     // Calls IWindow.  No unique initialization.
  308.     {
  309.         IWindow(itsDocument, itsWMgrWindow, canResize, canClose, disposeOnFree);
  310.     }
  311.  
  312. pascal void TBetterWindow :: Free()
  313.     // Calls inherited.  No unique cleanup.
  314.     {
  315.         inherited :: Free();
  316.     }
  317.  
  318. //----------------------------------------------------------------------------------------
  319. // DoPostCreate()
  320. //----------------------------------------------------------------------------------------
  321.  
  322. pascal void TBetterWindow :: DoPostCreate(    TDocument*        itsDocument)
  323.     // Calls inherited.  No unique initialization.
  324.     {
  325.         inherited :: DoPostCreate(itsDocument);
  326.     }
  327.  
  328. //----------------------------------------------------------------------------------------
  329. // SimpleStagger()
  330. //----------------------------------------------------------------------------------------
  331.  
  332. pascal void TBetterWindow :: SimpleStagger(    CPoint        delta,
  333.                                         short&        /*count*/)
  334. /*
  335.     This routine replaces the normal window-staggering algorithm with one that is
  336.     more intelligent.  Here, every window is, by default, offset from the position
  337.     of the window behind it.  If the calculated position is full, we continue to
  338.     offset until we find an open space.  If the space we end up with does not allow
  339.     the window to fit entirely on one monitor, we start again at the upper left-hand
  340.     corner of the monitor.
  341.     
  342.     This routine also resizes the standard document or discussion-viewer window to
  343.     fill the screen vertically, and takes care of restoring saved window positions.
  344.     __________________________________________________________________________________
  345.     Parameters:
  346.         delta:  The distance to offset the window from its parent.
  347.         count:  In the default version of this routine, this is a reference to a
  348.             global that says how many windows have been opened.  We don't use it.
  349.     
  350. */
  351.     {
  352.         CPoint                ulhc(7, 20);
  353.         CPoint                newPos;
  354.         WindowPtr            frontWindow;
  355.         CRect                temp;
  356.         CRect                monitorRect;
  357.         CPoint                localUlhc;
  358.         VRect                frame;
  359.         
  360.         // if fStaggered is true, then we've already done all this
  361.         if (this->fStaggered)
  362.             return;
  363.         
  364.         // otherwise, go ahead and set the flag now so we won't go through all this
  365.         // the next time
  366.         this->fStaggered = true;
  367.         
  368.         // tap in here to handle saved positions
  369.         if (PositionIsSaved()) {
  370.             if (RestorePosition())
  371.                 return;
  372.         }
  373.         
  374.         // take into account the menu bar when determining where to put the first
  375.         // window
  376.         ulhc.v += *(short*)MBarHeight;
  377.         
  378.         // find the front window and take note of its monitor rectangle
  379.         frontWindow = MAGetActiveWindow();
  380.         GetMonitorInfo(frontWindow, monitorRect);
  381.         
  382.         // if there is no front window, the new window will go in the upper left-hand
  383.         // corner of the main monitor, defined above
  384.         if (frontWindow == NULL) {
  385.             newPos = ulhc;
  386.             localUlhc = ulhc;
  387.         }
  388.         
  389.         // if there is a front window, the new one will be offset from it by the
  390.         // value of "delta" and the upper left-hand corner we use for positioning
  391.         // will be that of the monitor containing the most of the current front
  392.         // window (notice we have to adjust for the menu bar size)
  393.         else {
  394.             temp = (**((WindowPeek)frontWindow)->contRgn).rgnBBox;
  395.             newPos = temp[topLeft];
  396.             newPos += delta;
  397.             localUlhc = ulhc;
  398.             if (monitorRect[topLeft] != gZeroPt) {
  399.                 localUlhc += monitorRect[topLeft];
  400.                 localUlhc.v -= *(short*)MBarHeight;
  401.             }
  402.         }
  403.         this->MakePositionUnique(newPos, localUlhc, delta, monitorRect);
  404.         this->Locate(VPoint(newPos), false);
  405.         
  406. /*
  407.         // if this were a text-edit window, the following code would be useful
  408.         if ( [I'm a text-editing window] ) {
  409.             this->GetFrame(frame);
  410.             frame.bottom = monitorRect.bottom - 3;
  411.             this->Resize(frame.GetSize(), false);
  412.         }
  413. */
  414.     }
  415.  
  416. //----------------------------------------------------------------------------------------
  417. // GetStandardStateFrame()
  418. //----------------------------------------------------------------------------------------
  419.  
  420. pascal void TBetterWindow :: GetStandardStateFrame(    const VRect&    /*boundingRect*/,
  421.                                                 VRect&            stdFrame)
  422. /*
  423.     This routine is called by TWindow::Zoom() to get the frame size we're supposed
  424.     to zoom the window to.  We ignore the boundingRect passed in, and return the
  425.     window's new frame rectangle in stdFrame.
  426.     __________________________________________________________________________________
  427.     Parameters:
  428.         -> boundingRect:  Rectangle we're supposed to zoom to. (not used)
  429.         <- stdFrame:  The frame size we're actually going to zoom to (the return
  430.             value).
  431.     
  432. */
  433.     {
  434.         TView*        targetView;
  435.         TScroller*    targetScroller;
  436.         VRect        targetFrame;
  437.         VRect        windowFrame;
  438.         VPoint        difference;
  439.         GDHandle    monitor;
  440.         CRect        tempMonitorRect;
  441.         VRect        monitorRect;
  442.         VPoint        ulhc(7, 20);
  443.         
  444.         // get the window target view and figure out the difference between its
  445.         // current size and the window's current size (if the window target view is
  446.         // enclosed in a scroller, we're actually interested in the size of the
  447.         // scroller here instead [this probably won't work right if the window
  448.         // target view isn't the only thing in the scroller])
  449.         targetView = (TView*)this->GetWindowTarget();
  450.         targetScroller = targetView->GetScroller(false);
  451.         if (targetScroller == NULL)
  452.             targetView->GetFrame(targetFrame);
  453.         else
  454.             targetScroller->GetFrame(targetFrame);
  455.         this->GetFrame(windowFrame);
  456.         difference = windowFrame.GetSize() - targetFrame.GetSize();
  457.         
  458.         // calculate the target view's minimum frame size, and calculate a new
  459.         // frame rectangle for the window by adding the difference between the window
  460.         // size and the old frame size to the new frame size and making the window
  461.         // frame that new size
  462.         targetView->CalcMinFrame(targetFrame);
  463.         windowFrame[botRight] = windowFrame[topLeft] + targetFrame.GetSize() +
  464.                             difference;
  465.         if (windowFrame.GetLength(hSel) < fResizeLimits.left)
  466.             windowFrame.right = windowFrame.left + fResizeLimits.left;
  467.         if (windowFrame.GetLength(vSel) < fResizeLimits.top)
  468.             windowFrame.bottom = windowFrame.top + fResizeLimits.top;
  469.         
  470.         // get the rectangle of the monitor containing the largest part of the
  471.         // window (inset it a little to leave some slop on the sides and to leave
  472.         // room for the menu bar (if we have one) and the window's title bar)
  473.         // [NOTE:  GetMaxIntersectedDevice() will usually take the menu bar into
  474.         // account.  If it does, the else clause below will automatically adjust
  475.         // ulhc to take the menu bar into account too.]
  476.         monitor = this->GetMaxIntersectedDevice(tempMonitorRect);
  477.         monitorRect = VRect(tempMonitorRect);
  478.         if (monitorRect[topLeft] == gZeroVPt)
  479.             ulhc.v += *(short*)MBarHeight;
  480.         else
  481.             ulhc += monitorRect[topLeft];
  482.         monitorRect[topLeft] = ulhc;
  483.         monitorRect[botRight] -= VPoint(3, 3);
  484.         
  485.         // does the new window frame fit on that monitor?  If not, move the window
  486.         // to the upper left-hand corner of the monitor
  487.         if (!monitorRect.Contains(windowFrame)) {
  488.             windowFrame += ulhc - windowFrame[topLeft];
  489.             
  490.             // does it fit now?  If not, resize it so that as much of it as possible
  491.             // does
  492.             if (!monitorRect.Contains(windowFrame)) {
  493.                 windowFrame.bottom = Min(windowFrame.bottom, monitorRect.bottom);
  494.                 windowFrame.right = Min(windowFrame.right, monitorRect.right);
  495.             }
  496.         }
  497.         
  498. /*
  499.         // if this were a text-editing window, you'd probably want to do this
  500.         if ( [I'm a text-editing window] )
  501.             windowFrame.bottom = monitorRect.bottom;
  502. */
  503.         
  504.         // and return the result
  505.         stdFrame = windowFrame;
  506.     }
  507.  
  508. //----------------------------------------------------------------------------------------
  509. // Zoom()
  510. //----------------------------------------------------------------------------------------
  511.  
  512. pascal void TBetterWindow :: Zoom(    short        partCode)
  513. /*
  514.     Much as I hate to, I also had to override the TWindow::Zoom() method.  The reason
  515.     for this is that the regular zoom code doesn't take into account the possibility
  516.     that the window's standard state might have changed.
  517.     
  518.     That is, if I zoom the window (putting it in its zoomed, or "standard" state)
  519.     and then do something that would change the size of that standard state (such
  520.     as deleting items from it), when I click on the zoom box again, I don't want
  521.     to return to the window's original size (the un-zoomed, or "user" state); I want
  522.     to zoom the window to the new size of its standard state [if you don't believe me,
  523.     look at how window zooming works in the Finder].
  524.     
  525.     Therefore, when I'm going from the standard state back to the user state (partCode
  526.     == inZoomIn), I need to check first to see if the window's standard state has
  527.     changed size.  If it has,  then the current size becomes the new user state size,
  528.     and I resignal the zoom message on as a change from the (new) user state to the
  529.     (new) standard state (i.e., partCode becomes inZoomOut).
  530.     
  531.     Got that?
  532.     __________________________________________________________________________________
  533.     Parameters:
  534.         partCode:  If partCode == inZoomOut, I'm going from the user state to the
  535.             standard state (I'm "zooming the window); if partCode == inZoomIn, I'm
  536.             going from the standard state to the user state ("un-zooming" the window).
  537.     
  538. */
  539.     {
  540.         VRect        curFrame;
  541.         VRect        newStdStateFrame;
  542.         
  543.         if (partCode == inZoomOut)
  544.             inherited :: Zoom(partCode);
  545.         else {
  546.             this->GetFrame(curFrame);
  547.             this->GetStandardStateFrame(curFrame, newStdStateFrame);    // "curFrame" will be ignored
  548.             if (curFrame == newStdStateFrame)
  549.                 inherited :: Zoom(partCode);
  550.             else {
  551.                 if (fProcID & zoomDocProc)
  552.                     (*((WStateDataHandle)(((WindowPeek)fWMgrWindow)->dataHandle)))->
  553.                                         userState = curFrame.ToRect();
  554.                 inherited :: Zoom(inZoomOut);
  555.             }
  556.         }
  557.     }
  558.  
  559. //----------------------------------------------------------------------------------------
  560. // Close()
  561. //----------------------------------------------------------------------------------------
  562.  
  563. pascal void TBetterWindow :: Close()
  564. /*
  565.     This override is here simply to give me a place to tap in and save off the
  566.     position of the window as it goes down.
  567. */
  568.     {
  569.         SavePosition();
  570.         
  571.         inherited :: Close();
  572.     }
  573.  
  574. //----------------------------------------------------------------------------------------
  575. // USER-INITIAIATED MOVING AND RESIZING ROUTINES
  576. //----------------------------------------------------------------------------------------
  577.  
  578. pascal void TBetterWindow :: MoveByUser(    const VPoint&        theMouse)
  579.     {
  580.         inherited :: MoveByUser(theMouse);
  581.         fInDefaultPosition = false;
  582.     }
  583.  
  584. pascal void TBetterWindow :: ResizeByUser(    const VPoint&        theMouse)
  585.     {
  586.         inherited :: ResizeByUser(theMouse);
  587.         fInDefaultPosition = false;
  588.     }
  589.  
  590. pascal void TBetterWindow :: ZoomByUser(    const VPoint&        theMouse,
  591.                                         short                partCode)
  592.     {
  593.         inherited :: ZoomByUser(theMouse, partCode);
  594.         fInDefaultPosition = false;
  595.     }
  596.  
  597. //----------------------------------------------------------------------------------------
  598. // MarkPositionAsDirty()
  599. //----------------------------------------------------------------------------------------
  600.  
  601. pascal void TBetterWindow :: MarkPositionAsDirty()
  602. /*
  603.     This routine is here primarily to give external routines (such as the ones that
  604.     change the display parameters) a way to tell the window that it needs to save
  605.     its position again.
  606. */
  607.     {
  608.         fSavePosition = true;
  609.     }
  610.  
  611. //----------------------------------------------------------------------------------------
  612. // GetMonitorInfo()
  613. //----------------------------------------------------------------------------------------
  614.  
  615. pascal void TBetterWindow :: GetMonitorInfo(    WindowPtr        frontWindow,
  616.                                                 CRect&            monitorRect)
  617. /*
  618.     This routine is used by the SimpleStagger() routine to figure out which monitor
  619.     contains the largest part of the current front window and to take note of it
  620.     and its enclosing rectangle.  This way, all of SimpleStagger()'s other operations
  621.     will take place only on that monitor.
  622.     __________________________________________________________________________________
  623.     Parameters:
  624.         -> frontWindow:  WindowPtr of the frontmost visible window on the screen.
  625.             (NULL if no windows are visible in this application's layer).
  626.         <- monitorRect:  Enclosing rectangle (in QD global coordinates) of the
  627.             monitor containing the largest part of the window referenced by
  628.             frontWindow.
  629. */
  630.     {
  631.         GDHandle        monitor;
  632.         GrafPtr            wMgrPort;
  633.         TWindow*        frontTWindow;
  634.         
  635.         // if we don't have Color QuickDraw, we can't have multiple monitors, so
  636.         // by definition our entire universe is defined by the Window Manager port's
  637.         // portRect
  638.         if (!gConfiguration.hasColorQD) {
  639.             GetWMgrPort(wMgrPort);
  640.             monitorRect = wMgrPort->portRect;
  641.             return;
  642.         }
  643.         
  644.         // if there are no open windows, use the main monitor
  645.         if (frontWindow == NULL) {
  646.             monitor = GetMainDevice();
  647.             monitorRect = (**monitor).gdRect;
  648.         }
  649.         
  650.         // otherwise, use a method in TWindow to find out which monitor contains more
  651.         // of the frontmost window and then get info for it
  652.         else {
  653.             frontTWindow = WMgrToWindow(frontWindow);
  654.             if (frontTWindow == NULL) {
  655.                 monitor = GetMainDevice();
  656.                 monitorRect = (**monitor).gdRect;
  657.             }
  658.             else {
  659.                 monitor = frontTWindow->GetMaxIntersectedDevice(monitorRect);
  660.                 monitorRect.top -= *(short*)MBarHeight;
  661.             }
  662.         }
  663.     }
  664.  
  665. //----------------------------------------------------------------------------------------
  666. // MakePositionUnique()
  667. //----------------------------------------------------------------------------------------
  668.  
  669. pascal void TBetterWindow :: MakePositionUnique(    CPoint&            newPos,
  670.                                                 CPoint            ulhc,
  671.                                                 CPoint            delta,
  672.                                                 const CRect&    monitorRect)
  673. /*
  674.     This routine is used by the SimpleStagger() routine to make sure that the window
  675.     is positioned in a spot that isn't already occupied by another window and that
  676.     the window is positioned entirely on a single monitor.
  677.     __________________________________________________________________________________
  678.     Parameters:
  679.         <-> newPos:  On entry, the proposed position for the window.  On exit, the
  680.             actual position where the window should go.
  681.         -> ulhc:  The upper left-hand corner of the current monitor.  This is actually
  682.             the position closest to the monitor's upper left-hand corner where a
  683.             window can be placed.
  684.         -> delta:  A point repesenting the amount to offset the window from its parent.
  685.             This value is used when position the window toward the upper left-hand
  686.             corner.
  687.         -> monitorRect:  The rectangle defining the current monitor in global
  688.             coordinates.
  689.     
  690. */
  691.     {
  692.         CPoint            workPos(newPos);
  693.         CPoint            tempUlhc;
  694.         
  695.         // for as long as the proposed position is on the screen and another window
  696.         // occupies this position, keep advancing down and to the right until we
  697.         // either march off the screen or find an unoccupied position
  698.         while (monitorRect.Contains(workPos) && !IsPositionUnique(workPos))
  699.             workPos += delta;
  700.         
  701.         // if we've marched off the screen or if the unoccupied position won't
  702.         // allow the whole window to be on the screen, try again starting at the
  703.         // monitor's upper left-hand corner
  704.         if (!monitorRect.Contains(workPos) || !WillWindowFit(workPos, monitorRect)) {
  705.             if (newPos != ulhc) {
  706.                 workPos = ulhc;
  707.                 if (!IsPositionUnique(workPos)) {
  708.                     MakePositionUnique(workPos, ulhc, delta, monitorRect);
  709.                     
  710.                     // if we can't find an unoccupied position that will hold the
  711.                     // window after starting at the upper left-hand corner of the
  712.                     // monitor, try again, but start halfway between the first two
  713.                     // window positions on the monitor (the test here causes
  714.                     // recursive calls to fall out if they don't find an acceptable
  715.                     // position)
  716.                     if (workPos == ulhc) {
  717.                         workPos += CPoint(delta.h / 2, delta.v / 2);
  718.                         
  719.                         if (!IsPositionUnique(workPos)) {
  720.                             tempUlhc = workPos;
  721.                             MakePositionUnique(workPos, tempUlhc, delta, monitorRect);
  722.                             
  723.                             // if that doesn't work either, give up and just put the
  724.                             // window in the upper left-hand corner of the monitor
  725.                             if (workPos == tempUlhc)
  726.                                 workPos = ulhc;
  727.                         }
  728.                     }
  729.                 }
  730.             }
  731.             else
  732.                 workPos = newPos;
  733.         }
  734.         
  735.         // return the new position
  736.         newPos = workPos;
  737.     }
  738.  
  739. //----------------------------------------------------------------------------------------
  740. // IsPositionUnique()
  741. //----------------------------------------------------------------------------------------
  742.  
  743. pascal Boolean TBetterWindow :: IsPositionUnique(    CPoint            pos)
  744. /*
  745.     This routine is used by MakePositionUnique() to determine whether there is
  746.     already a window at the specified position.  Returns true if no currently
  747.     visible window in the Window Manager's window list has its upper left-hand
  748.     corner at the position specified by "pos".
  749. */
  750.     {
  751.         CWMgrIterator        iter;
  752.         WindowPtr            aWindowPtr;
  753.         CRect                windowRect;
  754.     
  755.         for (aWindowPtr = iter.FirstWMgrWindow(); iter.More(); aWindowPtr =
  756.                             iter.NextWMgrWindow())
  757.         if ((((WindowPeek)aWindowPtr)->visible) && IsDocumentWindow(aWindowPtr)) {
  758.             windowRect = (**((WindowPeek)aWindowPtr)->contRgn).rgnBBox;
  759.             if (windowRect[topLeft] == pos)
  760.                 return false;
  761.         }
  762.         return true;
  763.     }
  764.  
  765. //----------------------------------------------------------------------------------------
  766. // WillWindowFit()
  767. //----------------------------------------------------------------------------------------
  768.  
  769. pascal Boolean TBetterWindow :: WillWindowFit(    CPoint            pos,
  770.                                             const CRect&    monitorRect)
  771. /*
  772.     This routine is used by MakePositionUnique() to determine whether the window
  773.     will fit entirely within the specified monitorRect if its upper left-hand corner
  774.     is placed at "pos".  Returns true if the window will fit there, and false if it
  775.     won't.
  776. */
  777.     {
  778.         CRect            frame;
  779.         
  780.         frame[topLeft] = pos;
  781.         frame[botRight] = pos + this->fSize.ToPoint();
  782.         
  783.         // the insetting here takes into account the frame and drop shadow (we don't
  784.         // care about the title bar, since we're always going down and to the right
  785.         // and we know we started in a position where the title bar will work)
  786.         frame.Inset(CPoint(-2, -2));
  787.         
  788.         return monitorRect.Contains(frame);
  789.     }
  790.  
  791. //----------------------------------------------------------------------------------------
  792. // PositionIsSaved()
  793. //----------------------------------------------------------------------------------------
  794.  
  795. pascal Boolean TBetterWindow :: PositionIsSaved()
  796.     {
  797.         /*
  798.             This routine and the two below are left as an exercise for the reader.
  799.             My original version of this is proprietary.  The way we save and restore
  800.             window positions is specific to the kind of application we were
  801.             writing and can't be generalized.
  802.         */
  803.         
  804.         return false;
  805.     }
  806.  
  807. //----------------------------------------------------------------------------------------
  808. // RestorePosition()
  809. //----------------------------------------------------------------------------------------
  810.  
  811. pascal Boolean TBetterWindow :: RestorePosition()
  812.     {
  813.         // see comment in PositionIsSaved()
  814.         
  815.         return false;
  816.     }
  817.  
  818. //----------------------------------------------------------------------------------------
  819. // SavePosition()
  820. //----------------------------------------------------------------------------------------
  821.  
  822. pascal void TBetterWindow :: SavePosition()
  823.     {
  824.         // see comment in PositionIsSaved()
  825.     }
  826.  
  827.  
  828. //========================================================================================
  829. // GLOBAL Procedures
  830. //========================================================================================
  831.  
  832.  
  833. //----------------------------------------------------------------------------------------
  834. //  Globals
  835. //----------------------------------------------------------------------------------------
  836. TNothingApplication *gNothingApplication;        // The application object 
  837.  
  838.  
  839. //----------------------------------------------------------------------------------------
  840. // main:
  841. //----------------------------------------------------------------------------------------
  842. #pragma push
  843. #pragma processor 68000
  844. #pragma segment Main
  845.  
  846. void main()
  847. {
  848.     InitToolBox();
  849.  
  850.     if (ValidateConfiguration(gConfiguration))
  851.     {
  852.         InitUMacApp(8);                    // Initialize MacApp; 8 calls to MoreMasters
  853.  
  854.         InitUPrinting();
  855.         
  856.         gNothingApplication = new TNothingApplication;
  857.         gNothingApplication->INothingApplication();
  858.  
  859.         gNothingApplication->Run();
  860.     }
  861.     else
  862.         StdAlert(phUnsupportedConfiguration);
  863. } // main
  864.  
  865. #pragma pop
  866.  
  867.  
  868.